#!/usr/bin/env python 
# -*- coding: utf-8 -*-

import tkinter as tk
from tkinter import filedialog as fd, messagebox

from PIL import Image, ImageTk

from ImageMap import ImageMap
from WinBox import WinBox
from a2_solution import Game, Position, Grid, AdvancedMapLoader
from constants import HIGH_SCORES_FILE, MAX_ALLOWED_HIGH_SCORES, WIN_MESSAGE
from task1 import BasicGraphicalInterface, BANNER_HEIGHT


class ImageGraphicalInterface(BasicGraphicalInterface):
    """
    Using pack() to organize UI
    From top to bottom: banner_frame, main_frame, status_frame
    From left to right in main_frame: map_view, inventory_view
    From left to right in status_frame: chaser_label, timer_canvas, moves_canvas, button_frame, chasee_label
    """

    def __init__(self, root: tk.Tk, size: int):
        """
        First call BasicGraphicalInterface.__init__, then init status_frame
        Args:
            root:
            size:
        """
        BasicGraphicalInterface.__init__(self, root, size)

        self.width = int(self.windows_width / 5)
        self.height = 50
        self.seconds = 0
        self.moves = 0

        self.init_menu()
        self.init_status_bar()

    def init_menu(self):
        """
        init menu
        Returns:

        """
        menu = tk.Menu(self.root)
        menu.add_command(label='Restart game', command=self.restart_game)
        menu.add_command(label='Save game', command=self.ask_save_file)
        menu.add_command(label='Load game', command=self.ask_load_file)
        menu.add_command(label='Quit', command=self.quit_game)
        menu.add_command(label='High scores', command=self.show_high_scores)
        menu.add_command(label='Pause', command=self.stop_timer_event)
        menu.add_command(label='Resume', command=self.start_timer_event)

        self.root.config(menu=menu)

    def init_status_bar(self):
        """
        init status frame and things inside
        Returns:

        """
        status_frame = tk.Frame(self.root)
        status_frame.pack(fill=tk.BOTH)
        chaser_image = ImageTk.PhotoImage(Image.open('images/chaser.png'))
        chaser_label = tk.Label(status_frame, image=chaser_image)
        chaser_label.image = chaser_image
        chaser_label.pack(side=tk.LEFT, ipadx=45)

        chasee_image = ImageTk.PhotoImage(Image.open('images/chasee.png'))
        chasee_label = tk.Label(status_frame, image=chasee_image)
        chasee_label.image = chasee_image
        chasee_label.pack(side=tk.RIGHT, ipadx=45)

        self.timer_canvas = tk.Canvas(status_frame, height=self.height, width=self.width)
        self._draw_timer()
        self.timer_canvas.pack(side=tk.LEFT)

        self.moves_canvas = tk.Canvas(status_frame, height=self.height, width=self.width)
        self._draw_moves()
        self.moves_canvas.pack(side=tk.LEFT)

        button_frame = tk.Frame(status_frame, )
        self.btn_restart = tk.Button(button_frame, text='Restart Game', command=self.restart_game)
        btn_quit = tk.Button(button_frame, text='Quit Game', command=self.quit_game)
        button_frame.pack(side=tk.LEFT)
        self.btn_restart.pack(side=tk.TOP)
        btn_quit.pack(side=tk.TOP)

    def _step(self, game: Game):
        """
        add second counting
        Args:
            game:

        Returns:

        """
        BasicGraphicalInterface._step(self, game)
        self.seconds += 1

    def _move(self, game: Game, direction: str):
        """
        add moves counting
        Args:
            game:
            direction:

        Returns:

        """
        BasicGraphicalInterface._move(self, game, direction)
        self.moves += 1

    def init_banner(self, banner_frame: tk.Frame):
        """
        override method, showing image in banner_frame
        Args:
            banner_frame:

        Returns:

        """
        image = Image.open('images/banner.png')
        image = ImageTk.PhotoImage(image.resize((700, BANNER_HEIGHT)))
        # image = ImageTk.PhotoImage(image)
        label = tk.Label(banner_frame, image=image)
        label.image = image
        label.pack(fill=tk.BOTH)

    def init_map_view(self, main_frame: tk.Frame):
        """
        override method, change map_view initialization
        Args:
            main_frame:

        Returns:

        """
        return ImageMap(main_frame, (self.size, self.size))

    def draw(self, game: Game):
        """
        first call BasicGraphicalInterface.draw to draw things in task1,
        and then refresh timer_canvas and move_canvas
        Args:
            game:

        Returns:

        """
        BasicGraphicalInterface.draw(self, game)
        self._draw_timer()
        self._draw_moves()

    def _draw_timer(self):
        """
        refresh timer_canvas
        Returns:

        """
        self.timer_canvas.delete('all')
        self.timer_canvas.create_text(int(self.width / 2), int(self.height / 2), justify=tk.CENTER,
                                      text='Timer\n{} mins {} seconds'.format(int(self.seconds / 60),
                                                                              self.seconds % 60))

    def _draw_moves(self):
        """
        refresh move_canvas
        Returns:

        """
        self.moves_canvas.delete('all')
        self.moves_canvas.create_text(int(self.width / 2), int(self.height / 2), justify=tk.CENTER,
                                      text='Moves made\n{} moves'.format(self.moves))

    def restart_game(self):
        """
        stop all event and timer, clear timer and moves counter, reset game, start to play again
        Args:
            _:
            game:

        Returns:

        """
        self.stop_timer_event()
        self.moves = 0
        self.seconds = 0
        super().restart_game()


    def save_game(self,file):
        """
        save seconds, moves and map to txt file. the format just like maps but adding #12,2 at first line
        """
        grid = self.game.get_grid()
        size = grid.get_size()
        mappings = grid.get_mapping()
        mappings = {k: v.display() for k, v in mappings.items()}
        lines = ['#{},{}\n'.format(self.seconds, self.moves)]
        for i in range(size):
            line = ''.join([mappings.get(Position(j, i), ' ') for j in range(size)]) + '\n'
            lines.append(line)

        file.writelines(lines)

    def ask_save_file(self):
        """
        use asksaveasfile to open a file for saving
        """
        self.stop_timer_event()
        file_types = (
            ('text files', '.txt'),
            ('All files', '*.*')
        )

        file = fd.asksaveasfile(
            title='select file to save',
            initialdir='/',
            filetypes=file_types,
            defaultextension='.txt'
        )

        self.save_game(file)
        self.start_timer_event()

    def load_game(self, file_name):
        """
        load game,moves,seconds from file
        """
        with open(file_name, 'r') as f:
            lines = f.readlines()
        self.seconds, self.moves = lines[0][1:].split(',')
        self.seconds = int(self.seconds)
        self.moves = int(self.moves)

        map_lines = lines[1:]
        mapping, size = self.load_map_from_lines(map_lines)
        new_grid = Grid(size)
        map_loader = AdvancedMapLoader()
        for position, entity in mapping.items():
            new_grid.add_entity(Position(*position), map_loader.create_entity(entity))
        self.game.reset(new_grid)
        self.play(self.game)

    def load_map_from_lines(self, contents):

        """
        method idea from a2_solution
        """
        result = {}
        for y, line in enumerate(contents):
            for x, char in enumerate(line.strip("\n")):
                if char != " ":
                    result[(x, y)] = char

        return result, len(contents)

    def ask_load_file(self):
        """
        use askopenfilename to get file name
        """
        self.stop_timer_event()
        file_types = (
            ('text files', '.txt'),
            ('All files', '*.*')
        )

        file_name = fd.askopenfilename(
            title='select file to load',
            initialdir='/',
            filetypes=file_types,
            defaultextension='.txt'
        )

        self.load_game(file_name)

    def show_high_scores(self):
        lines = self._get_high_scores()
        msg = '\n'.join(lines)
        messagebox.showinfo("Top " + str(MAX_ALLOWED_HIGH_SCORES), msg)

    def _get_high_scores(self):
        try:
            with open(HIGH_SCORES_FILE, 'r') as f:
                lines = f.readlines()[:MAX_ALLOWED_HIGH_SCORES]
                lines = [x.strip() for x in lines]
        except:
            lines = []
        return lines

    def end_game_choice(self, game):
        won_game = game.has_won()
        lost_game = not won_game and game.has_lost()
        end_game = won_game or lost_game

        if end_game:
            self.stop_timer_event()
            if lost_game:
                return 'whatever' # do nothing to keep the final game state.
            msg = 'You won in {}m and {}s! Enter your name:'.format(int(self.seconds/60), self.seconds%60)
            winbox = WinBox(self.root, WIN_MESSAGE,msg)
            return 'whatever'
        return None